package bloodfang;

import java.util.Arrays;

public class CycleAssassination extends CycleRogue {

	private double autoattack_vendetta_mult = 1.;
	private double vendetta_mult = 1.;
	
	private double current_vendetta_cd = vendetta_cd;
	private double current_vanish_cd = vanish_cd;

	public CycleAssassination(Stats stats, Attacks attacks) {
		this.stats = stats;
		this.attacks = attacks;
		this.stats.getSettings().spec = Spec.ASSASSINATION;
	}

	@Override
	protected void generate_aps() {
		double last_aps = 0.;
		aps.clear();
		
		double base_vanish_cd = vanish_cd;
		double base_vendetta_cd = vendetta_cd;
		ProcInfo procInfo = null;
		for(String prefix : Procs.trinket_prefixes){
			if(stats.hasProc(prefix+"assurance_of_consequence"))
				procInfo = stats.getProcInfo(prefix+"assurance_of_consequence");
		}
		if(stats.getCooldownRecoveryRate() > 0){
			
		}
		if(procInfo != null){
			ProcData procData = stats.getProcData().get(procInfo.getProcName());
			double scale = get_scaling_value(procData,procInfo.getUpgradeLevel());
			double res = scale * 0.0098999999 / 100;
			base_vanish_cd /= (1. + res);
			base_vendetta_cd /= (1. + res);
		}
		current_vanish_cd = base_vanish_cd;
		current_vendetta_cd = base_vendetta_cd;
		
		double vendetta_duration = 20.;
		double vendetta_dmg = 0.3;
		if(stats.hasGlyph("vendetta")){
			vendetta_duration += 10.;
			vendetta_dmg -= 0.05;
		}
		double vendetta_uptime = vendetta_duration / current_vendetta_cd;
		vendetta_mult = 1 + vendetta_dmg * vendetta_uptime;
        
        double autoattack_duration = current_vendetta_cd;
        double autoattack_vendetta_overlap = current_vendetta_cd * vendetta_uptime;
        autoattack_vendetta_mult = 1 + vendetta_dmg * autoattack_vendetta_overlap / autoattack_duration;
		
		while(true){
			aps.clear(); // do not remove
			double energy_regen = get_energy_regen();
			
			// seal fate
			double crit_chance_mh = attacks.get_dmg("mutilate_mh").getCritChance();
			double crit_chance_oh = attacks.get_dmg("mutilate_oh").getCritChance();
			double mut_crit = 1 - ( 1 - crit_chance_mh) * ( 1 - crit_chance_oh);
			double dp_crit = attacks.get_dmg("dispatch").getCritChance();
			
			// blindside
			double blindside_proc_chance = 0.3 * stats.getMhStyleHitChance();
			
			double execute_time = stats.getSettings().execute_time;
			
			CycleAssassinationSetup mutilate = new CycleAssassinationSetup();
			mutilate.portion = 1 - execute_time;
			mutilate.main_cpg = "mutilate";
			mutilate.length = current_vendetta_cd;
			mutilate.energy_regen = energy_regen;
			
			
			CycleAssassinationSetup dispatch = new CycleAssassinationSetup();
			dispatch.portion = execute_time;
			dispatch.main_cpg = "dispatch";
			dispatch.length = current_vendetta_cd;
			dispatch.energy_regen = energy_regen;
			
			double env_buff_time = 0;
			for(CycleAssassinationSetup cycle : new CycleAssassinationSetup[]{mutilate,dispatch}){
				double snd_speed = 1.4;
				double speed = snd_speed * (1 + 0.01 * stats.getBuffed(Stat.hst) / RATING.HASTE_CONVERSION_RATING);
				if(stats.hasRaidBuff(RaidBuff.haste_buff))
					speed *= 1.1;
				if(stats.getRace() == Race.troll)
					speed *= 1 + .2 * 10. / 180;
				if(stats.getRace() == Race.golbin)
		            speed *= 1.01;
				speed *= (1 + 0.3 * 40. / stats.getSettings().FIGHT_LENGTH);	
				double total_speed = speed;
				
				double mh_hits = 0.;
				double oh_hits = 0.;
				double mh_swing_every = stats.getMh().getSpeed()/total_speed;
				double oh_swing_every = stats.getOh().getSpeed()/total_speed;

				mh_hits = cycle.length / mh_swing_every * this.stats.getMhHitChance();
				oh_hits = cycle.length / oh_swing_every * this.stats.getOhHitChance();
				aps.add("mh", mh_hits / current_vendetta_cd * cycle.portion);
				aps.add("oh", oh_hits / current_vendetta_cd * cycle.portion);
				
				double vw_regen = 10 * cycle.length / 2 * 0.75;
				double energy_available = cycle.energy_regen * cycle.length + vw_regen;
				
				double finisher_size = 5.;
				String cpg = cycle.main_cpg;
				double cp_per_cpg = 2;
				double base_ec_cpg = 55;
				if(cpg.equals("mutilate")){
					cp_per_cpg = 2 + mut_crit + 0.3 * (1 + dp_crit);
					base_ec_cpg = 55;
					if(stats.hasSetBonus(SetBonus.T16_2PC))
						base_ec_cpg -= 6 * mut_crit;
				}else if(cpg.equals("dispatch")){
					cp_per_cpg = 1 + dp_crit;
					base_ec_cpg = 30;
				}else{
					System.err.println("Error: " + cpg + " as Combo Point Builder is not valid.");
				}
				
				double ec_cpg = stats.getRealEnergyCost(base_ec_cpg);
				double avg_cpg_per_finisher = finisher_size / cp_per_cpg;
				
				double ec_env = stats.getRealEnergyCost(35);
				double ec_rup = stats.getRealEnergyCost(25);
				
				double avg_relentless_strikes = 5. * finisher_size;
				
				double rup_duration = 4 + 4 * finisher_size;
				double env_duration = 1 + 1 * finisher_size;
				if(stats.hasSetBonus(SetBonus.T15_2PC)){
					rup_duration += 4;
					env_duration += 1;
				}
				
				double energy_cost = avg_cpg_per_finisher * ec_cpg;
				double energy_cost_rup = energy_cost + ec_rup - avg_relentless_strikes;
				double energy_cost_env = energy_cost + ec_env - avg_relentless_strikes;
				
				double ruptures = cycle.length / rup_duration;
				double energy_left_for_env = energy_available - ruptures * energy_cost_rup;
				
				double attacks_so_far = ruptures * (1 + avg_cpg_per_finisher);
				double gcd = 1.0;
				
				double time_left = cycle.length - attacks_so_far * gcd;
				double sum_attacks_per_evi = avg_cpg_per_finisher + 1;
				double envenoms_time_limited = time_left / (sum_attacks_per_evi * gcd);
				double envenoms_energy_limited = energy_left_for_env / energy_cost_env;
				
				double envenoms = envenoms_energy_limited;
				if(envenoms_time_limited < envenoms_energy_limited){
					envenoms = envenoms_time_limited;
				}
				
				double cpg_per_cycle = avg_cpg_per_finisher * (ruptures + envenoms);
				double cpg_per_sec = cpg_per_cycle / current_vendetta_cd * cycle.portion;
				double envenoms_per_sec = envenoms / current_vendetta_cd * cycle.portion;
				double rupture_applications_per_sec = ruptures / current_vendetta_cd * cycle.portion;
				aps.add("rupture_applications", rupture_applications_per_sec);
				
				if(cpg.equals("mutilate")){
					aps.add(cpg+"_mh", cpg_per_sec);
					aps.add(cpg+"_oh", cpg_per_sec);
					aps.add("dispatch", cpg_per_sec * blindside_proc_chance);
				}else if(cpg.equals("dispatch")){
					aps.add(cpg, cpg_per_sec);
				}
				aps.add("envenom", envenoms_per_sec);
				double env_length = envenoms * env_duration;
//				System.out.println("env_length " + env_length);
//				System.out.println("cylce.length " + cycle.length);
				env_buff_time += Math.min(env_length, cycle.length + env_duration / 2) * cycle.portion;
			}
			aps.add("rupture", 1./2 );
			aps.add("venomous_wound", aps.get("rupture") * 0.75);
			
			double mh_aps = aps.get("mh") + aps.get("mutilate_mh") + aps.get("dispatch") + aps.get("envenom");
			double oh_aps = aps.get("oh") + aps.get("mutilate_oh");
			
			
			double env_buff_uptime = env_buff_time / current_vendetta_cd;
			//System.out.println("env_buff_uptime " + env_buff_uptime);
			
			double ip_aps = mh_aps * (0.5 + 0.15 * env_buff_uptime);
			ip_aps += oh_aps * (0.5 + 0.15 * env_buff_uptime);
			
			aps.add("instant_poison", ip_aps);
			aps.add("deadly_poison", 1./3);
			
			this.procs(mh_aps, oh_aps, ip_aps);
			
			if(Math.abs(last_aps - aps.sum()) <  PRECISION)
				break;
			last_aps = aps.sum();
		}
	}

	@Override
	protected double getSpecDamageMod(AttackData ab) {
		double dmgMod = 1.;
		dmgMod *= 1.25; // passive assassins resolve
		if(Arrays.asList("mh","oh").contains(ab.getAttack())){
			dmgMod *= this.autoattack_vendetta_mult;
		}
		else if(!ab.getAttack().contains("elemental_force"))
			dmgMod *= this.vendetta_mult;
		
		if(Arrays.asList("instant_poison","deadly_poison","envenom","venomous_wound").contains( ab.getAttack() )){
			dmgMod *= (1 + stats.get_assassination_mastery_multiplier());
		}
		return dmgMod;
	}

}
